package server;
import PBFT.PBFTCohort;
import com.google.common.base.Optional;
import com.google.common.collect.Sets;
import config.*;
import gameengine.*;
import gameengine.operations.NoOp;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.ObjectMapper;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Created by andrew on 12/1/14.
*/
public class PBFTServerInstance implements Runnable {
private static Logger LOG = LogManager.getLogger(PBFTServerInstance.class);
public static final int INITIAL_VIEW_ID = 0;
private static final int REPLICA_ID_ARG_POS = 0;
public PBFTCohortHandler handler;
public PBFTCohort.Processor processor;
private GroupConfigProvider<PBFTCohort.Client> configProvider;
public static String configFile;
private int replicaID;
private final String[] args;
private final PrivateKey privateKey;
private final Map<Integer, PublicKey> publicKeys;
private GameEngine<ChineseCheckersState> gameEngine;
public PBFTServerInstance(String[] args,
PrivateKey privateKey, Map<Integer, PublicKey> publicKeys,
String configFile) {
this.args = args;
this.privateKey = privateKey;
this.publicKeys = publicKeys;
this.configFile = configFile;
this.gameEngine = null;
}
private void configureLogging(GroupMember<PBFTCohort.Client> me) {
MDC.put("server-name", "[" + this.replicaID + "] " + me.getName() + ":" + me.getAddress().getHostName() + "/" + me.getAddress().getPort());
}
public void run() {
try {
replicaID = Integer.parseInt(args[REPLICA_ID_ARG_POS]);
configProvider = initializeConfigProvider(new File(configFile));
final GroupMember<PBFTCohort.Client> me = configProvider.getGroupMember(this.replicaID);
configProvider.getOtherGroupMembers().remove(me);
configureLogging(me);
LOG.info("Starting server on port: " + me.getAddress().getPort() + " with address: " + me.getAddress().getHostName());
// GameEngine<ChineseCheckersState> engine = new ChineseCheckersGameEngine(configProvider);
//this.gameEngine = new BenchmarkingGameEngine(configProvider);
this.gameEngine = new ChineseCheckersGameEngine(configProvider);
handler = new PBFTCohortHandler(configProvider, replicaID, me, gameEngine);
processor = new PBFTCohort.Processor(handler);
Runnable simple = new Runnable() {
public void run() {
simple(processor, me.getAddress());
}
};
new Thread(simple).start();
//executeNoOp(); // eventually replace with new Thread(new WebsocketChineseCheckersPlayer(new InetSocketAddress(portNum),decoder,gameEngine)).start();
new Thread(new WebsocketChineseCheckersPlayer(me.getWebsocketAddress(),gameEngine,replicaID)).start();
} catch (Exception x) {
x.printStackTrace();
}
}
private void executeNoOp() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LOG.info(configProvider.getMe().getReplicaID() + " " + 1);
if (configProvider.getMe().getReplicaID() == 1) {
gameEngine.requestCommit(new NoOp());
}
}
}).start();
}
public void notifyOnNextTurn() {
LOG.info("next turn");
gameEngine.requestCommit(new NoOp());
}
private void simple(PBFTCohort.Processor processor, InetSocketAddress address) {
try {
TServerTransport serverTransport = new TServerSocket(address);
TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor).maxWorkerThreads(1000).minWorkerThreads(100));
server.serve();
} catch (Exception e) {
e.printStackTrace();
}
}
private GroupConfigProvider<PBFTCohort.Client> initializeConfigProvider(File file) throws NoSuchMethodException, IOException, FileNotFoundException {
BufferedReader reader = new BufferedReader(new FileReader(file));
JsonFactory factory = new JsonFactory();
JsonParser jsonParser = factory.createJsonParser(reader);
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonParser);
JsonNode servers = root.get("servers");
GroupMember<PBFTCohort.Client> leader = null;
Set<GroupMember<PBFTCohort.Client>> clients = Sets.newHashSet();
int leaderId = root.get("primary").getIntValue();
Iterator<JsonNode> elements = servers.getElements();
GroupMember<PBFTCohort.Client> me = null;
while (elements.hasNext()) {
JsonNode server = elements.next();
GroupMember<PBFTCohort.Client> client = serverNodeToClient(server);
if (client.getReplicaID() == leaderId) {
leader = client;
}
if (client.getReplicaID() == replicaID) {
me = client;
}
clients.add(client);
}
return new StaticGroupConfigProvider<PBFTCohort.Client>(leader, me, clients, INITIAL_VIEW_ID);
}
private GroupMember<PBFTCohort.Client> serverNodeToClient(JsonNode server) throws NoSuchMethodException, UnknownHostException {
int id = server.get("id").getIntValue();
JsonNode isMock = server.get("mock");
if(isMock == null || !isMock.getBooleanValue()) return new NetworkedGroupMember<>(
server.get("name").getTextValue(),
id,
new InetSocketAddress(server.get("hostname").getTextValue(), server.get("port").getIntValue()),
new InetSocketAddress(server.get("hostname").getTextValue(), server.get("ws_port").getIntValue()),
PBFTCohort.Client.class,
publicKeys.get(id),
Optional.fromNullable(id == this.replicaID ? this.privateKey : null)
);
else {
return new StubbedGroupMember<PBFTCohort.Client>(
server.get("name").getTextValue(),
id,
new InetSocketAddress(server.get("hostname").getTextValue(), server.get("port").getIntValue()),
new InetSocketAddress(server.get("hostname").getTextValue(), server.get("ws_port").getIntValue()),
PBFTCohort.Client.class,
publicKeys.get(id),
Optional.fromNullable(id == this.replicaID ? this.privateKey : null)
);
}
}
public GroupConfigProvider<PBFTCohort.Client> getConfigProvider() {
return configProvider;
}
}